package com.wandousoushu.jiusen.ui.book.changesource

import android.app.Application
import android.os.Bundle
import android.os.Handler
import androidx.lifecycle.MutableLiveData
import com.wandousoushu.jiusen.MyApplication
import com.wandousoushu.jiusen.R
import com.wandousoushu.jiusen.base.BaseViewModel
import com.wandousoushu.jiusen.booksource.constant.AppPattern
import com.wandousoushu.jiusen.booksource.constant.PreferKey
import com.wandousoushu.jiusen.booksource.data.entities.Book
import com.wandousoushu.jiusen.booksource.data.entities.SearchBook
import com.wandousoushu.jiusen.booksource.model.BookSourceManager
import com.wandousoushu.jiusen.booksource.model.SearchBookManager
import com.wandousoushu.jiusen.booksource.model.WebBook
import com.wandousoushu.jiusen.help.AppConfig
import com.wandousoushu.jiusen.help.coroutine.Coroutine
import com.wandousoushu.jiusen.util.getPrefBoolean
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.debug
import java.util.concurrent.Executors

class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
    private var searchPool =
        Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
    val handler = Handler()
    val searchStateData = MutableLiveData<Boolean>()
    val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
    var name: String = ""
    var author: String = ""
    private var task: Coroutine<*>? = null
    private var screenKey: String = ""
    private val searchBooks = hashSetOf<SearchBook>()
    private var postTime = 0L
    private val sendRunnable = Runnable { upAdapter() }


    fun initData(arguments: Bundle?) {
        arguments?.let { bundle ->
            bundle.getString("name")?.let {
                name = it
            }
            bundle.getString("author")?.let {
                author = it.replace(AppPattern.authorRegex, "")
            }
        }
    }

    fun loadDbSearchBook() {
        execute {
            SearchBookManager.GetInstance().getSearchBookByNameAuthor(name, author).let {
                searchBooks.addAll(it!!)
                if (it.size <= 1) {
                    upAdapter()
                    search()
                } else {
                    upAdapter()
                }
            }
        }
    }

    @Synchronized
    private fun upAdapter() {
        if (System.currentTimeMillis() >= postTime + 500) {
            handler.removeCallbacks(sendRunnable)
            postTime = System.currentTimeMillis()
            val books = searchBooks.toList()
            searchBooksLiveData.postValue(books.sortedBy { it.originOrder })
        } else {
            handler.removeCallbacks(sendRunnable)
            handler.postDelayed(sendRunnable, 500 - System.currentTimeMillis() + postTime)
        }
    }

    private fun searchFinish(searchBook: SearchBook) {
        SearchBookManager.GetInstance().AddSearckBook(searchBook)
        //App.db.searchBookDao().insert(searchBook)
        if (screenKey.isEmpty()) {
            searchBooks.add(searchBook)
        } else if (searchBook.name.contains(screenKey)) {
            searchBooks.add(searchBook)
        }
        upAdapter()
    }

    fun search() {
        task = execute {
            //val bookSourceList = MyApplication.instance.getBookSourceList()
            val bookSourceList = BookSourceManager.GetInstance().getBookSourceList()
            for (item in bookSourceList) {
                //task取消时自动取消 by （scope = this@execute）
                WebBook(item).searchBook(name, scope = this@execute, context = searchPool)
                    .timeout(30000L)
                    .onSuccess(IO) {
                        it.forEach { searchBook ->
                            if (searchBook.name == name && searchBook.author == author) {
                                if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
                                    if (searchBook.tocUrl.isEmpty()) {
                                        loadBookInfo(searchBook.toBook())
                                    } else {
                                        loadChapter(searchBook.toBook())
                                    }
                                } else {
                                    searchFinish(searchBook)
                                }
                                return@onSuccess
                            }
                        }
                    }
            }
        }.onStart {
            searchStateData.postValue(true)
        }.onCancel {
            searchStateData.postValue(false)
        }

        task?.invokeOnCompletion {
            searchStateData.postValue(false)
        }
    }

    private fun loadBookInfo(book: Book) {
        execute {
            BookSourceManager.GetInstance().getBookSource(book.origin)?.let { bookSource ->
                WebBook(bookSource).getBookInfo(book, this)
                    .onSuccess {
                        loadChapter(it)
                    }.onError {
                        debug { context.getString(R.string.error_get_book_info) }
                    }
            } ?: debug { context.getString(R.string.error_no_source) }
        }
    }

    private fun loadChapter(book: Book) {
        execute {
            BookSourceManager.GetInstance().getBookSource(book.origin)?.let { bookSource ->
                WebBook(bookSource).getChapterList(book, this)
                    .onSuccess(IO) { chapters ->
                        if (chapters.isNotEmpty()) {
                            book.latestChapterTitle = chapters.last().title
                            val searchBook: SearchBook = book.toSearchBook()
                            searchFinish(searchBook)
                        }
                    }.onError {
                        debug { context.getString(R.string.error_get_chapter_list) }
                    }
            } ?: debug { R.string.error_no_source }
        }
    }

    /**
     * 筛选
     */
    fun screen(key: String?) {
        execute {
            screenKey = key ?: ""
            if (key.isNullOrEmpty()) {
                loadDbSearchBook()
            } else {
                val items = SearchBookManager.GetInstance().getChangeSourceSearch(name, author, screenKey)
                //val items = App.db.searchBookDao().getChangeSourceSearch(name, author, screenKey)
                searchBooks.clear()
                searchBooks.addAll(items!!)
                upAdapter()
            }
        }
    }

    fun stopSearch() {
        if (task?.isActive == true) {
            task?.cancel()
        } else {
            search()
        }
    }

    override fun onCleared() {
        super.onCleared()
        searchPool.close()
    }

    fun disableSource(searchBook: SearchBook) {
        execute {
//            App.db.bookSourceDao().getBookSource(searchBook.origin)?.let { source ->
//                source.enabled = false
//                App.db.bookSourceDao().update(source)
//            }
//            searchBooks.remove(searchBook)
//            upAdapter()
        }
    }

}